﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json;

namespace ServerListerner.Helper
{
    internal static class DataMap
    {

        public static object? ObjectToObjectParam(this object? obj, ParameterInfo parameter)
        {
            try
            {
                obj = obj.JsonElementToObject();
                Type dataType = obj.GetType();
                var decType = parameter.ParameterType;
                if (decType.IsCustomClass())
                {
                    if (decType.IsArray)
                    {
                        Type elementType = dataType.GetElementType();
                        var _dec_obj = (IList)Activator.CreateInstance(dataType);
                        Type _obj_type = _dec_obj.GetType();
                        var method = _obj_type.GetMethod("Add");
                        (obj as List<object>)?.ForEach(s => {
                            var s_type = s.GetType();
                            if (s_type == typeof(JsonElement))
                                method?.Invoke(_dec_obj, new object[] { JsonElementToObject(s) });
                            else if (s_type == typeof(Dictionary<string, object>) || s_type == typeof(Dictionary<string, object?>))
                                method?.Invoke(_dec_obj, new object[] { CopyObjectByType(s, elementType) });
                            else
                                method?.Invoke(_dec_obj, new object[] { Convert.ChangeType(s, elementType) });
                        });
                        Array array = Array.CreateInstance(elementType, _dec_obj.Count);
                        _dec_obj.CopyTo(array, 0);
                        return array;
                    }
                    else if (decType.IsGenericType && decType.GetGenericTypeDefinition() == typeof(List<>))
                    {
                        Type elementType = decType.GetGenericArguments().FirstOrDefault();
                        if (elementType != null)
                        {
                            var _dec_obj = (IList)Activator.CreateInstance(decType);
                            Type _obj_type = _dec_obj.GetType();
                            var method = _obj_type.GetMethod("Add");
                            (obj as List<object>)?.ForEach(s => {
                                var s_type = s.GetType();
                                if (s_type == typeof(JsonElement))
                                    method?.Invoke(_dec_obj, new object[] { JsonElementToObject(s) });
                                else if (s_type == typeof(Dictionary<string, object>) || s_type == typeof(Dictionary<string, object?>))
                                    method?.Invoke(_dec_obj, new object[] { CopyObjectByType(s, elementType) });
                                else
                                    method?.Invoke(_dec_obj, new object[] { Convert.ChangeType(s, elementType) });
                            });
                            return _dec_obj;
                        }
                        else
                            return dataType.DefaultValue();
                    }
                    else
                        return obj.CopyObjectByType(decType);
                }
                if (decType.IsEnum)
                    return Enum.TryParse(decType, obj == null ? string.Empty : dataType == decType ? obj.ToString() : string.Empty, out object? result) ? result : null;
                if (decType == typeof(string))
                    return obj == null ? string.Empty : obj.ToString();
                if (decType == typeof(Guid))
                    return obj == null ? Guid.Empty : Guid.TryParse(obj.ToString(), out Guid result) ? result : Guid.Empty;
                if (obj != null)
                    return Convert.ChangeType(obj, decType);
                return Activator.CreateInstance(decType);

            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public static object? StrToObjectParam(this string? str, ParameterInfo parameter)
        {
            try
            {
                if (parameter.ParameterType.IsCustomClass())
                    return string.IsNullOrEmpty(str) ? null : str.JsonToObject(parameter.ParameterType);
               if (parameter.ParameterType.IsEnum)
                    return Enum.TryParse(parameter.ParameterType, str, out object? result) ? result : null;
                if (parameter.ParameterType == typeof(string))
                    return string.IsNullOrEmpty(str) ? string.Empty : str;
                if (parameter.ParameterType == typeof(Guid))
                    return string.IsNullOrEmpty(str) ? Guid.Empty : Guid.TryParse(str, out Guid result) ? result : Guid.Empty;
                if (!string.IsNullOrEmpty(str))
                    return Convert.ChangeType(str, parameter.ParameterType);
                return Activator.CreateInstance(parameter.ParameterType);

            }
            catch (Exception ex)
            {
                return null;
            }
        }
        internal static Dictionary<string, object> FormDataToDictionary(this string _str, string SliptLint)
        {
            Dictionary<string, object> dict = new Dictionary<string, object>();
            List<string> lines = _str.Split(new string[] { SliptLint }, StringSplitOptions.None).Select(s => s.Replace("--\r\n", string.Empty).Replace("\r\n--", string.Empty)).ToList();
            lines = lines.Where(s => !string.IsNullOrEmpty(s) && s != "--").ToList();
            lines.ForEach(s =>
            {
                string[] str = s.Split(new string[] { "\r", "\n", "\t", "\0" }, StringSplitOptions.None).Where(s => !string.IsNullOrEmpty(s)).ToArray();
                if (str.Length > 0 && str[0].Contains("Content-Disposition: form-data; name="))
                {
                    string[] _title = str[0].Split(new string[] { "form-data;" }, StringSplitOptions.None);
                    if (_title.Length == 2)
                    {
                        _title = _title[1].Split('=');
                        if (_title.Length == 2)
                            if (!dict.ContainsKey(_title[1]))
                                dict.Add(_title[1].Replace("\"", string.Empty), str.LastOrDefault());
                    }


                }
            });
            return dict;
        }
        public static object? CopyObjectByType(this object? _obj, Type _type)
        {
            _obj = _obj.JsonElementToObject();
            var sourceType = _obj?.GetType();
            PropertyInfo[]? sourcePropperties = sourceType?.GetProperties();
            var dec_obj = Activator.CreateInstance(_type);
            Type _objType = dec_obj.GetType();
            var desery = _objType.GetProperties();
            foreach (var item in desery)
            {
                try
                {
                    var _source = sourcePropperties?.Where(s => s.Name == item.Name).FirstOrDefault();
                    if (_source != null || sourceType == typeof(Dictionary<string, object>))
                    {
                        object? value = _source != null ? _source.GetValue(_obj)
                            : sourceType == typeof(Dictionary<string, object>) ?
                            (_obj as Dictionary<string, object>).Where(s => s.Key.ToLower() == item.Name.ToLower()).FirstOrDefault().Value :
                            null;
                        if (value == null)
                        {
                            item.SetValue(dec_obj, item.DefaultValue());
                            continue;
                        }
                        Type tmp_type = value.GetType();
                        if (tmp_type.IsCustomClass())
                        {
                            if (item.PropertyType.IsArray)
                            {

                                Type elementType = item.PropertyType.GetElementType();
                                var _dec_obj = (IList)Activator.CreateInstance(item.PropertyType);
                                Type _obj_type = _dec_obj.GetType();
                                var method = _obj_type.GetMethod("Add");
                                (value as List<object>)?.ForEach(s => {
                                    var s_type = s.GetType();
                                    if (s_type == typeof(JsonElement))
                                        method?.Invoke(_dec_obj, new object[] { JsonElementToObject(s) });
                                    else if (s_type == typeof(Dictionary<string, object>) || s_type == typeof(Dictionary<string, object?>))
                                        method?.Invoke(_dec_obj, new object[] { CopyObjectByType(s, elementType) });
                                    else
                                        method?.Invoke(_dec_obj, new object[] { Convert.ChangeType(s, elementType) });
                                });
                                Array array = Array.CreateInstance(elementType, _dec_obj.Count);
                                _dec_obj.CopyTo(array, 0);
                                item.SetValue(dec_obj, array);
                            }
                            else if (item.PropertyType.IsGenericType && item.PropertyType.GetGenericTypeDefinition() == typeof(List<>))
                            {
                                Type elementType = item.PropertyType.GetGenericArguments().FirstOrDefault();
                                if (elementType != null)
                                {
                                    var _dec_obj = (IList)Activator.CreateInstance(item.PropertyType);
                                    Type _obj_type = _dec_obj.GetType();
                                    var method = _obj_type.GetMethod("Add");
                                    (value as List<object>)?.ForEach(s => {
                                        var s_type = s.GetType();
                                        if (s_type == typeof(JsonElement))
                                            method?.Invoke(_dec_obj, new object[] { JsonElementToObject(s) });
                                        else if (s_type == typeof(Dictionary<string, object>) || s_type == typeof(Dictionary<string, object?>))
                                            method?.Invoke(_dec_obj, new object[] { CopyObjectByType(s, elementType) });
                                        else
                                            method?.Invoke(_dec_obj, new object[] { Convert.ChangeType(s, elementType) });
                                    });
                                    item.SetValue(dec_obj, _dec_obj);
                                }
                                else
                                    item.SetValue(dec_obj, item.DefaultValue());
                            }
                            else
                                item.SetValue(dec_obj, value.CopyObjectByType(tmp_type));
                        }
                        else if (tmp_type == typeof(string))
                            item.SetValue(dec_obj, value.ToString());
                        else if (tmp_type.IsEnum)
                            item.SetValue(dec_obj, Enum.TryParse(item.PropertyType, (value ?? string.Empty).ToString(), out object result) ? result : 0);
                        else if (tmp_type == typeof(Guid))
                            item.SetValue(dec_obj, Guid.TryParse(value.ToString(), out Guid result) ? result : Guid.Empty);
                        else
                            item.SetValue(dec_obj, Convert.ChangeType(value, item.PropertyType));
                    }
                    else
                        item.SetValue(dec_obj, item.DefaultValue());
                }
                catch (Exception ex)
                {
                    item.SetValue(dec_obj, item.DefaultValue());
                }
            }
            return dec_obj;
        }
        public static object? JsonElementToObject(this object? obj)
        {
            if (obj == null) return null;
            Type type = obj.GetType();
            if (type == typeof(string))
            {
                var obj_str= obj.ToString().JsonToObject<object>();
                type = (obj_str??obj).GetType();
            }
            if (type != typeof(JsonElement)) return obj;
            JsonElement element = (JsonElement)obj;
            object _obj = new object();
            JsonElement.ObjectEnumerator enumerateObject;
            List<PropertyInfo> sourcePropperties = new List<PropertyInfo>();
            switch (element.ValueKind)
            {
                case JsonValueKind.Object:
                    enumerateObject = element.EnumerateObject();
                    Dictionary<string, object> pairs = new Dictionary<string, object>();
                    foreach (JsonProperty item in enumerateObject)
                    {
                        var tmp = JsonElementToObject(item.Value);
                        pairs.Add(item.Name, tmp);
                    }
                    _obj = pairs;
                    break;
                case JsonValueKind.Array:
                    var enumerateArray = element.EnumerateArray();
                    List<object> array = new List<object>();
                    foreach (var item in enumerateArray)
                    {
                        array.Add(JsonElementToObject(item));
                    }
                    _obj = array;
                    break;
                case JsonValueKind.String:
                    if (element.TryGetDateTime(out DateTime str_value))
                        _obj = str_value;
                    else if (element.TryGetDateTimeOffset(out DateTimeOffset str_value1))
                        _obj = str_value1;
                    else if (element.TryGetGuid(out Guid str_value2))
                        _obj = str_value2;
                    else
                        _obj = element.GetString();
                    break;
                case JsonValueKind.Number:
                    if (element.TryGetByte(out byte value))
                        _obj = value;
                    else if (element.TryGetDecimal(out decimal value1))
                        _obj = value1;
                    else if (element.TryGetDouble(out double value2))
                        _obj = value2;
                    else if (element.TryGetInt16(out short value3))
                        _obj = value3;
                    else if (element.TryGetInt32(out int value4))
                        _obj = value4;
                    else if (element.TryGetInt64(out long value5))
                        _obj = value5;
                    else if (element.TryGetSByte(out sbyte value6))
                        _obj = value6;
                    else if (element.TryGetSingle(out float value7))
                        _obj = value7;
                    else if (element.TryGetUInt16(out ushort value8))
                        _obj = value8;
                    else if (element.TryGetUInt32(out uint value9))
                        _obj = value9;
                    else if (element.TryGetUInt64(out ulong value10))
                        _obj = value10;
                    else
                        _obj = 0;
                    break;
                case JsonValueKind.True:
                    _obj = true;
                    break;
                case JsonValueKind.False:
                    _obj = false;
                    break;
                case JsonValueKind.Null:
                    _obj = null;
                    break;
                default:
                    _obj = null;
                    break;
            }
            return _obj;
        }
        public static object? DefaultValue(this PropertyInfo? obj)
        {
            try
            {
                object? type = null;
                if (obj.PropertyType == typeof(string))
                    type = string.Empty;
                else if (obj.PropertyType.IsValueType)
                {
                    if (obj.PropertyType.IsEnum)
                        type = Enum.ToObject(obj.PropertyType, 0);
                    type = Activator.CreateInstance(obj.PropertyType);
                }
                return type;
            }
            catch (Exception ex)
            {
                return null;
            }

        }
        public static object? DefaultValue(this Type? obj)
        {
            try
            {
                object? type = null;
                if (obj == typeof(string))
                    type = string.Empty;
                else if (obj.IsValueType)
                {
                    if (obj.IsEnum)
                        type = Enum.ToObject(obj, 0);
                    type = Activator.CreateInstance(obj);
                }
                return type;
            }
            catch (Exception ex)
            {
                return null;
            }

        }
    }
}
